﻿using Sidvall.Data;
using Sidvall.IdentityManager.Security;
using System.Threading.Tasks;

namespace Sidvall.IdentityManager.Model.DataPortalManagers
{
    public partial class AspNetUsersManager<TEntity, TList> : Sidvall.Business.BusinessManagerBase
    {
        #region Public Members

        #region CreateUserAsync

        public async Task<TEntity> CreateUserAsync(string name, string email, bool emailConfirmed, string phoneNumber, bool phoneNumberConfirmed)
        {
            var item = new CommandCriteria()
            {
                CriteriaId = ParameterManager.CreateUserCommandId,
                Parameters = new ParameterCollection(),
            };
            item.Parameters.Add(ParameterManager.NameParameterName, name);
            item.Parameters.Add(ParameterManager.EmailParameterName, email);
            item.Parameters.Add(ParameterManager.EmailConfirmedParameterName, emailConfirmed);
            item.Parameters.Add(ParameterManager.PhoneNumberParameterName, phoneNumber);
            item.Parameters.Add(ParameterManager.PhoneNumberConfirmedParameterName, phoneNumberConfirmed);
            item = await ExecuteAsync(item).ConfigureAwait(false);
            var user = item.Parameters.ValueToObject<Sidvall.IdentityManager.Net.Services.V1.AspNetUsers>(ParameterManager.ResultParameterKey, null);
            if (user != null)
            {
                var settings = CopySettings.FullCopyAll(DataRowContextOption.None, DataRowState.Unchanged);
                return (TEntity)this.DataPortalManager.GenericEntityFacade.AspNetUsersManager.Copy(user, settings);
            }
            return default(TEntity);
        }

        #endregion
        #region CreateSecurityStampAsync

        public async Task<string> CreateSecurityStampAsync(string userId)
        {
            var item = new CommandCriteria()
            {
                CriteriaId = ParameterManager.CreateSecurityStampCommandId,
                Parameters = new ParameterCollection(),
            };
            item.Parameters.Add(ParameterManager.UserIdParameterName, userId);
            item = await ExecuteAsync(item).ConfigureAwait(false);
            return item.Parameters.ValueToString(ParameterManager.ResultParameterKey, null);
        }

        #endregion
        #region GenerateTokenAsync

        public async Task<string> GenerateTokenAsync(string userId, string purpose, string tokenProvider)
        {
            var item = new CommandCriteria()
            {
                CriteriaId = ParameterManager.GenerateTokenCommandId,
                Parameters = new ParameterCollection(),
            };
            item.Parameters.Add(ParameterManager.UserIdParameterName, userId);
            item.Parameters.Add(ParameterManager.PurposeParameterName, purpose);
            item.Parameters.Add(ParameterManager.TokenProviderParameterName, tokenProvider);
            item = await ExecuteAsync(item).ConfigureAwait(false);
            return item.Parameters.ValueToString(ParameterManager.ResultParameterKey, null);
        }

        #endregion
        #region GetUserByIUserIdAsync

        public async Task<TEntity> GetUserByIUserIdAsync(string userId, bool loadUserClaims, bool loadUserLogins)
        {
            var criteria = CriteriaManager.NewFetchCriteria<global::Sidvall.IdentityManager.Data.Fields.AspNetUsersField, global::Sidvall.IdentityManager.Data.Relations.AspNetUsersRelation>();
            criteria.Filter = new global::Sidvall.Data.Filter<global::Sidvall.IdentityManager.Data.Fields.AspNetUsersField>(LogicalOperator.Or);
            criteria.Filter.AddFilterItem(global::Sidvall.IdentityManager.Data.Fields.AspNetUsersField.Id, Sidvall.Data.ComparisonOperator.Equal, userId);
            criteria.IncludedEntities = new RelationCollection<Data.Relations.AspNetUsersRelation>();
            if (loadUserClaims)
                criteria.IncludedEntities.Add(Data.Relations.AspNetUsersRelation.AspNetUserClaims);
            if (loadUserLogins)
                criteria.IncludedEntities.Add(Data.Relations.AspNetUsersRelation.AspNetUserLogins);
            return await GetItemAsync(criteria).ConfigureAwait(false);
        }

        #endregion
        #region GetUserFromEmailAsync

        public async Task<TEntity> GetUserByEmailAsync(string email, bool loadUserClaims, bool loadUserLogins)
        {
            var criteria = CriteriaManager.NewFetchCriteria<global::Sidvall.IdentityManager.Data.Fields.AspNetUsersField, global::Sidvall.IdentityManager.Data.Relations.AspNetUsersRelation>();
            criteria.Filter = new global::Sidvall.Data.Filter<global::Sidvall.IdentityManager.Data.Fields.AspNetUsersField>(LogicalOperator.Or);
            criteria.Filter.AddFilterItem(global::Sidvall.IdentityManager.Data.Fields.AspNetUsersField.Email, Sidvall.Data.ComparisonOperator.Equal, email);
            criteria.IncludedEntities = new RelationCollection<Data.Relations.AspNetUsersRelation>();
            if (loadUserClaims)
                criteria.IncludedEntities.Add(Data.Relations.AspNetUsersRelation.AspNetUserClaims);
            if (loadUserLogins)
                criteria.IncludedEntities.Add(Data.Relations.AspNetUsersRelation.AspNetUserLogins);
            return await GetItemAsync(criteria).ConfigureAwait(false);
        }

        #endregion
        #region GetUserFromUserNameAsync

        public async Task<TEntity> GetUserByUserNameAsync(string userName, bool loadUserClaims, bool loadUserLogins)
        {
            var criteria = CriteriaManager.NewFetchCriteria<global::Sidvall.IdentityManager.Data.Fields.AspNetUsersField, global::Sidvall.IdentityManager.Data.Relations.AspNetUsersRelation>();
            criteria.Filter = new global::Sidvall.Data.Filter<global::Sidvall.IdentityManager.Data.Fields.AspNetUsersField>(LogicalOperator.Or);
            criteria.Filter.AddFilterItem(global::Sidvall.IdentityManager.Data.Fields.AspNetUsersField.UserName, Sidvall.Data.ComparisonOperator.Equal, userName);
            criteria.IncludedEntities = new RelationCollection<Data.Relations.AspNetUsersRelation>();
            if (loadUserClaims)
                criteria.IncludedEntities.Add(Data.Relations.AspNetUsersRelation.AspNetUserClaims);
            if (loadUserLogins)
                criteria.IncludedEntities.Add(Data.Relations.AspNetUsersRelation.AspNetUserLogins);
            return await GetItemAsync(criteria).ConfigureAwait(false);
        }

        #endregion
        #region GetUsersAsync

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
        public async Task<TList> GetUsersAsync(string textFilter, bool sortReversed, bool loadUserClaims, bool loadUserLogins, int? rowCount, int? rowStartIndex)
        {
            var criteria = CriteriaManager.NewFetchListCriteria<global::Sidvall.IdentityManager.Data.Fields.AspNetUsersField, global::Sidvall.IdentityManager.Data.Relations.AspNetUsersRelation>();
            if (rowCount != null)
            {
                criteria.RowCount = rowCount.Value;
                criteria.RowStartIndex = rowStartIndex;
            }

            // Sort
            criteria.SortItems = new SortItemCollection<Data.Fields.AspNetUsersField>();
            if (sortReversed)
            {
                criteria.SortItems.Add(Data.Fields.AspNetUsersField.UserName, false);
            }
            else
            {
                criteria.SortItems.Add(Data.Fields.AspNetUsersField.UserName);
            }

            // Filter
            criteria.Filter = new Filter<Data.Fields.AspNetUsersField>();
            if (!string.IsNullOrWhiteSpace(textFilter))
            {
                var subFilter = criteria.Filter.AddChildFilter(LogicalOperator.Or);
                subFilter.AddFilterItem(Data.Fields.AspNetUsersField.UserName, ComparisonOperator.Like, textFilter);
                subFilter.AddFilterItem(Data.Fields.AspNetUsersField.Email, ComparisonOperator.Like, textFilter);
                subFilter.AddFilterItem(Data.Fields.AspNetUsersField.PhoneNumber, ComparisonOperator.Like, textFilter);
            }

            // IncludedEntities
            criteria.IncludedEntities = new RelationCollection<Data.Relations.AspNetUsersRelation>();
            if (loadUserClaims)
                criteria.IncludedEntities.Add(Data.Relations.AspNetUsersRelation.AspNetUserClaims);
            if (loadUserLogins)
                criteria.IncludedEntities.Add(Data.Relations.AspNetUsersRelation.AspNetUserLogins);

            var items = await GetItemsAsync(criteria).ConfigureAwait(false);
            return items;
        }

        #endregion
        #region GetUsersFromLoginAsync

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
        public async Task<TList> GetUsersFromLoginAsync(string loginProvider, string providerKey, bool loadUserClaims, bool loadUserLogins)
        {
            var criteria = CriteriaManager.NewFetchListCriteria<global::Sidvall.IdentityManager.Data.Fields.AspNetUsersField, global::Sidvall.IdentityManager.Data.Relations.AspNetUsersRelation>(ParameterManager.FindUsersFromLoginCommandId, null);

            // Parameters
            criteria.Parameters = new ParameterCollection();
            criteria.Parameters.Add(ParameterManager.LoginProviderParameterKey, loginProvider);
            criteria.Parameters.Add(ParameterManager.ProviderKeyParameterKey, providerKey);

            var items = await GetItemsAsync(criteria).ConfigureAwait(false);
            if (loadUserClaims)
                await LoadAspNetUserClaimsAsync(items.DataItems, false, false, null,null, null).ConfigureAwait(false);
            if (loadUserLogins)
                await LoadAspNetUserLoginsAsync(items.DataItems, false, false, null, null, null).ConfigureAwait(false);

            return items;
        }

        #endregion
        #region ResetPasswordAsync

        public async Task<bool> ResetPasswordAsync(string userId, string newPassword)
        {
            var item = new CommandCriteria()
            {
                CriteriaId = ParameterManager.ResetPasswordCommandId,
                Parameters = new ParameterCollection(),
            };
            item.Parameters.Add(ParameterManager.UserIdParameterName, userId);
            item.Parameters.Add(ParameterManager.PasswordParameterName, newPassword);
            item = await ExecuteAsync(item).ConfigureAwait(false);
            return true;
        }

        #endregion
        #region SendConfirmEmailAsync

        public async Task<bool> SendConfirmEmailAsync(string userId)
        {
            var item = new CommandCriteria()
            {
                CriteriaId = ParameterManager.SendConfirmEmailCommandId,
                Parameters = new ParameterCollection(),
            };
            item.Parameters.Add(ParameterManager.UserIdParameterName, userId);
            item = await ExecuteAsync(item).ConfigureAwait(false);
            return true;
        }

        #endregion
        #region SendResetPasswordEmailAsync

        public async Task<bool> SendResetPasswordEmailAsync(string userId)
        {
            var item = new CommandCriteria()
            {
                CriteriaId = ParameterManager.SendResetPasswordEmailCommandId,
                Parameters = new ParameterCollection(),
            };
            item.Parameters.Add(ParameterManager.UserIdParameterName, userId);
            item = await ExecuteAsync(item).ConfigureAwait(false);
            return true;
        }

        #endregion
        #region VerifyPasswordAsync

        public async Task<PasswordVerificationResult> VerifyPasswordAsync(string passwordHash, string password)
        {
            var item = new CommandCriteria()
            {
                CriteriaId = ParameterManager.VerifyPasswordCommandId,
                Parameters = new ParameterCollection(),
            };
            item.Parameters.Add(ParameterManager.PasswordHashParameterName, passwordHash);
            item.Parameters.Add(ParameterManager.PasswordParameterName, password);
            item = await ExecuteAsync(item).ConfigureAwait(false);
            return item.Parameters.ValueToEnum<PasswordVerificationResult>(ParameterManager.ResultParameterKey, PasswordVerificationResult.Failed);
        }

        #endregion
        #region VerifyUserPasswordAsync

        public async Task<PasswordVerificationResult> VerifyUserPasswordAsync(string userId, string password)
        {
            var item = new CommandCriteria()
            {
                CriteriaId = ParameterManager.VerifyUserPasswordCommandId,
                Parameters = new ParameterCollection(),
            };
            item.Parameters.Add(ParameterManager.UserIdParameterName, userId);
            item.Parameters.Add(ParameterManager.PasswordParameterName, password);
            item = await ExecuteAsync(item).ConfigureAwait(false);
            return item.Parameters.ValueToEnum<PasswordVerificationResult>(ParameterManager.ResultParameterKey, PasswordVerificationResult.Failed);
        }

        #endregion
        #region VerifyTokenAsync

        public async Task<bool> VerifyTokenAsync(string userId, string purpose, string token, string tokenProvider)
        {
            var item = new CommandCriteria()
            {
                CriteriaId = ParameterManager.VerifyTokenCommandId,
                Parameters = new ParameterCollection(),
            };
            item.Parameters.Add(ParameterManager.UserIdParameterName, userId);
            item.Parameters.Add(ParameterManager.PurposeParameterName, purpose);
            item.Parameters.Add(ParameterManager.TokenParameterName, token);
            item.Parameters.Add(ParameterManager.TokenProviderParameterName, tokenProvider);
            item = await ExecuteAsync(item).ConfigureAwait(false);
            return item.Parameters.ValueToBoolean(ParameterManager.ResultParameterKey, false);
        }

        #endregion

        #endregion
    }
}